//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using ManagementStudioServices.Interfaces;
using Microsoft.Research.ScientificWorkflow.ManagementStudioControls.Common;
using Microsoft.Research.ScientificWorkflow.ManagementStudioModels;
using Microsoft.Research.ScientificWorkflow.TridentUtilities;
using Microsoft.Research.ScientificWorkflow.UIDesigner.WpfComposite;
using Microsoft.Practices.Unity;
using SR = Microsoft.Research.DataLayer;
using config;
using System;

namespace Microsoft.Research.ScientificWorkflow.ManagementStudioControls.WorkflowManagerModule
{
    /// <summary>
    /// The Presentation logic for the running jobs table.
    /// </summary>
    public class RunningWorkflowsListPresenter : IListPresenter, INotifyPropertyChanged
    {
        #region Private variables
        /// <summary>
        /// The unity container instance.
        /// </summary>
        private IUnityContainer container;

        /// <summary>
        /// The workflow manager service instance.
        /// </summary>
        private IWorkflowManagerService workflowManagerService;

        /// <summary>
        /// The Title on the tableview.
        /// </summary>
        private string listViewTitle;

        /// <summary>
        /// The search view for the available workflows filter.
        /// </summary>
        private UIElement searchView;

        /// <summary>
        /// The view which displays the workflows.
        /// </summary>
        private UIElement runningWorkflowsListView;

        /// <summary>
        /// The menu items for the listview items.
        /// </summary>
        private Collection<MenuItem> contextMenuItems;

        /// <summary>
        /// The selected index of the listview.
        /// </summary>
        private int selectedIndex;

        /// <summary>
        /// Flag to check if items were retreived in the query. 
        /// </summary>
        private bool emptyList;

        /// <summary>
        /// The items to be displayed on the tableview.
        /// </summary>
        private ObservableCollection<object> items;

        static string selectAll = ManagementStudioResourceManager.GetString("SelectAllItems");
        string jobName = string.Empty;
        string jobStatus = selectAll;
        string nodeName = selectAll;

        Collection<string> jobNodeList = new Collection<string>();
        Collection<string> statusList = new Collection<string>();


        #endregion

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="container">The unity contaier</param>
        /// <param name="view">The center pane view of the presenter.</param>
        /// <param name="service">The workflow manager service</param>
        public RunningWorkflowsListPresenter(IUnityContainer container, RunningWorkflowsListView view, IWorkflowManagerService service)
        {
            this.container = container;
            this.runningWorkflowsListView = view;
            this.workflowManagerService = service;

            this.items = new ObservableCollection<object>();
            this.ListViewTitle = ManagementStudioResourceManager.GetString("TableTitleRunningWorkflows");
            this.searchView = this.container.Resolve<RunningWorkflowsSearchView>();
            (this.runningWorkflowsListView as RunningWorkflowsListView).DataContext = this;
            (this.searchView as RunningWorkflowsSearchView).DataContext = this;

            // Initialize commands
            this.TerminateWorkflowCommand = new DelegateCommand<JobViewModel>(this.ExecuteTerminateWorkflow, this.JobCanExecute);
            this.ShowMonitorCommand = new DelegateCommand<JobViewModel>(this.ExecuteShowMonitorCommand, this.JobCanExecute);
            this.RefreshListCommand = new DelegateCommand<object>(p => { this.DisplayItems(string.Empty); });

            this.ResumeWorkflowCommand = new DelegateCommand<JobViewModel>(this.ExecuteResumeWorkflowCommand, p => { return false; });
            this.PauseWorkflowCommand = new DelegateCommand<JobViewModel>(this.ExecutePauseWorkflowCommand, p => { return false; });

            //search commmand
            this.SearchCommand = new DelegateCommand<object>(p => this.SearchCommandExecute());

            this.Items.CollectionChanged += this.Items_CollectionChanged;
            this.SetContextMenuItems();

            jobNodeList.Insert(0, selectAll);
            //Populate the job status
            statusList.Add(selectAll);
            statusList.Add(ManagementStudioResourceManager.GetString("JobStatusRunning"));
            statusList.Add(ManagementStudioResourceManager.GetString("JobStatusStartPending"));

            Collection<SR.Machine> nodeList = workflowManagerService.GetAllMachines(null);
            foreach (SR.Machine node in nodeList)
            {
                if (!jobNodeList.Contains(node.Name.ToUpper()))
                {
                    jobNodeList.Add(node.Name.ToUpper());
                }
            }
        }

        #region Public Properties

        /// <summary>
        /// The command executed when the search button is clicked.
        /// </summary>
        public DelegateCommand<object> SearchCommand { get; private set; }

        /// <summary>
        /// The command executed when the user terminates a workflow.
        /// </summary>
        public DelegateCommand<JobViewModel> TerminateWorkflowCommand { get; private set; }

        /// <summary>
        /// The command executed to display the monitor window to show the progress of the workflow.
        /// </summary>
        public DelegateCommand<JobViewModel> ShowMonitorCommand { get; private set; }

        /// <summary>
        /// The command executed when the user pauses a workflow.
        /// </summary>
        public DelegateCommand<JobViewModel> PauseWorkflowCommand { get; private set; }

        /// <summary>
        /// The command executed when the user resumes a workflow.
        /// </summary>
        public DelegateCommand<JobViewModel> ResumeWorkflowCommand { get; private set; }

        /// <summary>
        /// The command to refresh the list.
        /// </summary>
        public DelegateCommand<object> RefreshListCommand { get; private set; }

        /// <summary>
        /// Property to check if items were retreived in the query.
        /// </summary>
        public bool EmptyList
        {
            get
            {
                return this.emptyList;
            }
            private set
            {
                this.emptyList = value;
                this.OnNotifyPropertyChanged("EmptyList");
            }
        }

        /// <summary>
        /// Job name for search
        /// </summary>
        public string JobName
        {
            get
            {
                return jobName;
            }
            set
            {
                jobName = value;
                this.OnNotifyPropertyChanged("JobName");
            }
        }


        /// <summary>
        /// Selected Job status for search
        /// </summary>
        public string JobStatus
        {
            get
            {
                return jobStatus;
            }
            set
            {
                if (null == value)
                {
                    jobStatus = selectAll;
                }
                else
                {
                    jobStatus = value;
                }
                this.OnNotifyPropertyChanged("JobStatus");
            }
        }


        /// <summary>
        /// Selected node name for search
        /// </summary>
        public string NodeName
        {
            get
            {
                return nodeName;
            }
            set
            {
                if (null == value)
                {
                    nodeName = selectAll;
                }
                else
                {
                    nodeName = value;
                }
                this.OnNotifyPropertyChanged("NodeName");
            }
        }

        /// <summary>
        /// Job Status list search
        /// </summary>
        public Collection<string> StatusList
        {
            get
            {
                return statusList;
            }
            set
            {
                statusList = value;
            }
        }


        /// <summary>
        /// Job node list for search
        /// </summary>
        public Collection<string> JobNodeList
        {
            get
            {
                return jobNodeList;
            }
            set
            {
                jobNodeList = value;
            }
        }

        #endregion


        #region IListPresenter Members

        /// <summary>
        /// The items to be displayed in the table view.
        /// </summary>
        public ObservableCollection<object> Items
        {
            get
            {
                return this.items;
            }
        }

        /// <summary>
        /// The title of the table view.
        /// </summary>
        public string ListViewTitle
        {
            get
            {
                return this.listViewTitle;
            }
            set
            {
                this.listViewTitle = value;
                this.OnNotifyPropertyChanged("ListViewTitle");
            }
        }


        /// <summary>
        ///  The search view for the available workflows filter.
        /// </summary>
        public UIElement SearchView
        {
            get
            {
                return this.searchView;
            }
        }


        /// <summary>
        /// The view which displays the workflows.
        /// </summary>
        public UIElement ListView
        {
            get
            {
                return this.runningWorkflowsListView;
            }
        }

        /// <summary>
        /// Display the items in the table view.
        /// </summary>
        /// <param name="queryString"></param>
        public void DisplayItems(string queryString)
        {
            try
            {
                Items.Clear();

                Collection<SR.Job> jobList = this.workflowManagerService.GetRunningWorkflows(null);

                // Set the selected Index. Show the empty message if no items found.
                if (jobList != null && jobList.Count > 0)
                {
                    this.EmptyList = false;

                    // Set status.
                    this.container.Resolve<IStatusMessageControl>().SetText(ManagementStudioResourceManager.GetString("StatusJobsCount",
                        jobList.Count.ToString(CultureInfo.CurrentUICulture)));

                    // Add to items list.
                    foreach (SR.Job job in jobList)
                    {
                        try
                        {
                            JobViewModel jobViewmodel = new JobViewModel(job);
                            Items.Add(jobViewmodel);
                        }
                        catch (System.Security.SecurityException)
                        {
                            // Ignore: The user might not have permissions on the workflow.
                        }
                    }
                    this.SelectedIndex = 0;
                }
                else
                {
                    // Set the empty list property to true. Shows the empty list message and disables the buttons.
                    this.EmptyList = true;
                    this.container.Resolve<IStatusMessageControl>().SetText(string.Empty);
                }

                this.RaiseCanExecuteEvents();
            }
            catch (TridentCustomException ex)
            {
                TridentMessageBox.ShowTridentErrorMessageBox(ex.Message);
            }
        }


        /// <summary>
        /// The menu items for the listview items.
        /// </summary>
        public Collection<System.Windows.Controls.MenuItem> MenuItemCollection
        {
            get
            {
                return this.contextMenuItems;
            }
        }

        /// <summary>
        /// The selected index of the list.
        /// </summary>
        public int SelectedIndex
        {
            get
            {
                return this.selectedIndex;
            }
            set
            {
                this.selectedIndex = value;
                this.OnNotifyPropertyChanged("SelectedIndex");
            }
        }

        #endregion

        #region INotifyPropertyChanged Members

        /// <summary>
        /// The property changed event. Fired when a proprty value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Fires the property changed event.
        /// </summary>
        /// <param name="propertyName">Name of the property which has been modified.</param>
        private void OnNotifyPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion

        #region Private methods

        /// <summary>
        /// Terminate the job specified. 
        /// </summary>
        /// <param name="selectedJob">The job to be terminated.</param>
        private void ExecuteTerminateWorkflow(JobViewModel selectedJob)
        {
            try
            {
                if (selectedJob != null)
                {
                    // Get a confirmation from the user.
                    MessageBoxResult result = TridentMessageBox.ShowTridentConfirmationYesNoMessageBox(
                        ManagementStudioResourceManager.GetString("TerminateJobConfirmation"));

                    if (result == MessageBoxResult.Yes)
                    {
                        this.workflowManagerService.TerminateJob(selectedJob.JobId);

                        // Remove the item from the list. Will be automatically removed from the listview.
                        this.Items.Remove(selectedJob);
                    }
                }
            }
            catch (TridentCustomException ex)
            {
                TridentMessageBox.ShowTridentErrorMessageBox(ex.Message);
            }
        }

        /// <summary>
        /// Show the monitor window for the job selected.
        /// </summary>
        /// <param name="selectedJob">The job selected.</param>
        private void ExecuteShowMonitorCommand(JobViewModel selectedJob)
        {
            try
            {
                if (selectedJob != null)
                {
                    switch (selectedJob.MachineScheduled.MachineType)
                    {
                        case SR.MachineType.Virtual:
                            // Show message indicating that the monitor window cannot be shown as 
                            //  the machine is virtual and no blackboard service is enabled for execution.
                            TridentMessageBox.ShowTridentInformationMessageBox(
                                ManagementStudioResourceManager.GetString("MonitoringNotAllowed"));
                            break;
                        case SR.MachineType.HPCCluster:
                        case SR.MachineType.Desktop:
                        case SR.MachineType.HPCComputeNode:
                        default:
                            this.workflowManagerService.OpenMonitor(selectedJob.JobId);
                            break;
                    }
                }
            }
            catch (TridentCustomException ex)
            {
                TridentMessageBox.ShowTridentErrorMessageBox(ex.Message);
            }
        }

        /// <summary>
        /// Pause the workflow.
        /// </summary>
        /// <param name="selectedjob">The workflow instance to be paused.</param>
        private void ExecutePauseWorkflowCommand(JobViewModel selectedjob)
        {
        }

        /// <summary>
        /// Resume the workflow.
        /// </summary>
        /// <param name="selectedjob">The workflow instance to be resumed.</param>
        private void ExecuteResumeWorkflowCommand(JobViewModel selectedjob)
        {
        }

        /// <summary>
        /// Checks if the workflow can be terminated. It enables and disables the buttons and menuitems.
        /// </summary>
        /// <param name="selectedJob">The job selected.</param>
        /// <returns>True if the workflow can be terminated. False otherwise.</returns>
        private bool JobCanExecute(JobViewModel selectedJob)
        {
            if (this.EmptyList)
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// The menu items for the listview items.
        /// </summary>
        private void SetContextMenuItems()
        {
            this.contextMenuItems = new Collection<MenuItem>();

            MenuItem showMonitorMenuItem = new MenuItem();
            showMonitorMenuItem.Header = ManagementStudioResourceManager.GetString("MenuItemHeaderShowMonitor");
            showMonitorMenuItem.Command = this.ShowMonitorCommand;
            showMonitorMenuItem.Icon = IconUtilities.GetIcon(@"pack://Application:,,,/Microsoft.Research.ScientificWorkflow.TridentUtilities;component/Images/ManagementStudio/showmonitorwindow.png");
            this.contextMenuItems.Add(showMonitorMenuItem);

            MenuItem pauseMenuitem = new MenuItem();
            pauseMenuitem.Header = ManagementStudioResourceManager.GetString("MenuItemHeaderPause");
            pauseMenuitem.Command = this.PauseWorkflowCommand;
            pauseMenuitem.Icon = IconUtilities.GetIcon(@"pack://Application:,,,/Microsoft.Research.ScientificWorkflow.TridentUtilities;component/Images/ManagementStudio/pause.png");
            this.contextMenuItems.Add(pauseMenuitem);

            MenuItem resumeMenuitem = new MenuItem();
            resumeMenuitem.Header = ManagementStudioResourceManager.GetString("MenuItemHeaderResume");
            resumeMenuitem.Command = this.ResumeWorkflowCommand;
            resumeMenuitem.Icon = IconUtilities.GetIcon(@"pack://Application:,,,/Microsoft.Research.ScientificWorkflow.TridentUtilities;component/Images/ManagementStudio/resume.png");
            this.contextMenuItems.Add(resumeMenuitem);

            MenuItem terminateMenuitem = new MenuItem();
            terminateMenuitem.Header = ManagementStudioResourceManager.GetString("MenuItemHeaderTerminate");
            terminateMenuitem.Command = this.TerminateWorkflowCommand;
            terminateMenuitem.Icon = IconUtilities.GetIcon(@"pack://Application:,,,/Microsoft.Research.ScientificWorkflow.TridentUtilities;component/Images/ManagementStudio/terminate.png");
            this.contextMenuItems.Add(terminateMenuitem);
        }

        /// <summary>
        /// Raise the can execute events so that the UI elements can be enabled/disabled.
        /// </summary>
        private void RaiseCanExecuteEvents()
        {
            this.TerminateWorkflowCommand.RaiseCanExecuteChanged();
            this.ShowMonitorCommand.RaiseCanExecuteChanged();
            this.PauseWorkflowCommand.RaiseCanExecuteChanged();
            this.ResumeWorkflowCommand.RaiseCanExecuteChanged();
        }

        /// <summary>
        /// Event handler for the collection changed event on the Items collection.
        /// </summary>
        /// <param name="sender">The source.</param>
        /// <param name="e">Event arguments</param>
        void Items_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            ICollection<object> collection = sender as ICollection<object>;

            // If collection is empty then disable the buttons.
            this.EmptyList = (collection.Count == 0);
            this.RaiseCanExecuteEvents();
        }


        /// <summary>
        /// Dummy implementation for the search click button.
        /// </summary>
        private void SearchCommandExecute()
        {
            try
            {
                Items.Clear();

                // This is the place wNodeNamehere the workflows are retrieved from the registry. The search call ideally goes here.
                // Get the required items.
                SR.Job.ISearch.ISearchClause filter = null;
                List<SR.Job.ISearch.ISearchClause> filterArray = new List<SR.Job.ISearch.ISearchClause>();
                if (!string.IsNullOrEmpty(this.JobName))
                {
                    filterArray.Add(SR.Job.ISearch.Name(SR.StringField.Condition.Contains, this.JobName));
                }

                if (!string.IsNullOrEmpty(this.JobStatus) && this.JobStatus != selectAll)
                {
                    SR.JobStatus status = (SR.JobStatus)System.Enum.Parse(typeof(SR.JobStatus), this.JobStatus);
                    filterArray.Add(SR.Job.ISearch.Status(Microsoft.Research.DataLayer.EnumFieldBase.Condition.Equals, status));
                }

                if (!string.IsNullOrEmpty(this.NodeName) && this.NodeName != selectAll)
                {
                    Collection<SR.Machine> nodes = workflowManagerService.GetAllMachinesWithName(this.NodeName);
                    SR.Job.ISearch.ISearchClause machineClause = null;
                    foreach (SR.Machine node in nodes)
                    {
                        if (null == machineClause)
                        {
                            machineClause = SR.Job.ISearch.Machine(Microsoft.Research.DataLayer.RelationFieldBase.Condition.Equals, node);
                        }
                        else
                        {
                            machineClause = SR.Job.ISearch.OR(machineClause, SR.Job.ISearch.Machine(Microsoft.Research.DataLayer.RelationFieldBase.Condition.Equals, node));
                        }
                    }
                    filterArray.Add(machineClause);
                }

                if (filterArray.Count > 1)
                {
                    filter = SR.Job.ISearch.AND(filterArray.ToArray());
                }
                else
                {
                    filter = filterArray.Count > 0 ? filterArray[0] : null;
                }

                Collection<SR.Job> jobList = workflowManagerService.GetRunningWorkflows(filter);

                // Set the selected Index. Show the empty message if no items found.
                if (jobList != null && jobList.Count > 0)
                {
                    this.SelectedIndex = 0;
                    this.EmptyList = false;
                    // Set status.
                    this.container.Resolve<IStatusMessageControl>().SetText(ManagementStudioResourceManager.GetString("StatusWorkflowCount",
                        jobList.Count.ToString(CultureInfo.CurrentUICulture)));

                    // Add the items to the list.
                    // As the Items property is a observable collection and is bound to the listview. Any changes here will be shown in the listview. 
                    foreach (SR.Job job in jobList)
                    {
                        JobViewModel jobViewmodel = new JobViewModel(job);
                        Items.Add(jobViewmodel);
                    }
                }
                else
                {
                    // Set the empty list property to true. Shows the empty list message and disables the buttons.
                    this.EmptyList = true;
                    this.container.Resolve<IStatusMessageControl>().SetText(string.Empty);
                }
                this.RaiseCanExecuteEvents();
            }
            catch (SR.RecordNotFoundException)
            {
                // Set status.
                this.container.Resolve<IStatusMessageControl>().SetText(ManagementStudioResourceManager.GetString("StatusWorkflowCount",
                    0.ToString(CultureInfo.CurrentUICulture)));
            }
            catch (TridentCustomException ex)
            {
                TridentMessageBox.ShowTridentErrorMessageBox(ex.Message);
            }
        }
        #endregion
    }
}
